package com.lechi.web.controller;

import com.lechi.web.common.annotation.RepeatSubmit;
import com.lechi.web.common.core.controller.BaseController;
import com.lechi.web.common.core.domain.AjaxResult;
import com.lechi.web.common.core.page.TableDataInfo;
import com.lechi.web.constants.Constants;
import com.lechi.web.framework.util.GuavaCacheUtil;
import com.lechi.web.runner.CacheCmdParseMsg;
import com.lechi.web.runner.SendModbusCmdSchedule;
import com.lechi.web.service.IIotDeviceCmdService;
import com.lechi.web.service.IIotDeviceService;
import com.lechi.web.service.IIotGenCmdAndCacheAndScheduleService;
import com.lechi.web.system.domain.*;
import com.lechi.web.vo.IotDeviceCmdQueryVo;
import com.lechi.web.vo.IotDeviceCmdUpdateVo;
import com.lechi.web.vo.IotDeviceCmdVo;
import com.lechi.web.vo.IotMapVo;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

/**
 * 读写指令配置Controller
 *
 * @author yangguang
 */
@RestController
@RequestMapping("/iot/cmd")
@RequiredArgsConstructor
@Slf4j
public class IotDeviceCmdController extends BaseController {

    private final IIotDeviceCmdService iotDeviceCmdService;

    private final IIotGenCmdAndCacheAndScheduleService iotHexCmdCreateService;

    private final CacheCmdParseMsg cacheCmdParseMsg;

    private final SendModbusCmdSchedule sendModbusCmdSchedule;

    private final IIotDeviceService iotDeviceService;

    /**
     * 读写指令配置列表
     *
     * @param iotDeviceCmdVo the iot device cmd vo
     * @return the table data info
     */
    @GetMapping("/list")
    //    @PreAuthorize("@ss.hasPermi('iot:cmd:query')")
    public TableDataInfo list(@Validated IotDeviceCmdQueryVo iotDeviceCmdVo) {
        startPage();
        IotDeviceCmdBo iotDeviceCmdBo = new IotDeviceCmdBo();
        BeanUtils.copyProperties(iotDeviceCmdVo, iotDeviceCmdBo);
        List<IotDeviceCmdBo> retList = iotDeviceCmdService.selectIotDeviceCmdList(iotDeviceCmdBo);
        return getDataTable(retList);
    }


    /**
     * 读写指令配置列表
     *
     * @param iotDeviceCmdVo the iot device cmd vo
     * @return the ajax result
     */
    @GetMapping("/group")
    //    @PreAuthorize("@ss.hasPermi('iot:cmd:group')")
    public AjaxResult group(@Validated IotDeviceCmdQueryVo iotDeviceCmdVo) {
        startPage();
        IotDeviceCmdBo iotDeviceCmdBo = new IotDeviceCmdBo();
        BeanUtils.copyProperties(iotDeviceCmdVo, iotDeviceCmdBo);
        List<IotDeviceCmdBo> retList = iotDeviceCmdService.selectIotDeviceCmdList(iotDeviceCmdBo);
        Map<String, List<IotDeviceCmdBo>> mapList = retList.stream().collect(Collectors.groupingBy(IotDeviceCmdBo::getSensorGroupName));
        final List<Map<String, Object>> list = mapList.entrySet().stream().map(entry -> Map.of("groupName", entry.getKey(), "groupData", entry.getValue())).toList();
        return AjaxResult.success(list).put("total", retList.size());
    }

    /**
     * Monitor ajax result.
     *
     * @param iotDeviceCmdVo the iot device cmd vo
     * @return the ajax result
     */
    @GetMapping("/monitor")
//    @PreAuthorize("@ss.hasPermi('iot:cmd:group')")
    public AjaxResult monitor(IotDeviceCmdVo iotDeviceCmdVo) {
        startPage();
        var iotDeviceCmdBo = new IotDeviceCmdBo();
        BeanUtils.copyProperties(iotDeviceCmdVo, iotDeviceCmdBo);
        Map<String, Object> map = new HashMap<>();
        List<IotDeviceCmdMonitor> list = iotDeviceCmdService.selectIotDeviceMonitorList(iotDeviceCmdBo);
        list.forEach(iotDeviceCmdMonitor -> {
            iotDeviceCmdMonitor.setSensorGroupId(iotDeviceCmdMonitor.getSensorGroupId() != null ? iotDeviceCmdMonitor.getSensorGroupId() : "");
            map.putIfAbsent(iotDeviceCmdMonitor.getSensorGroupId(), iotDeviceCmdMonitor.getSensorGroupName());
            Optional<IotMonitorDataBo> optional = Optional.ofNullable(GuavaCacheUtil.getMonitorDataMap().get(iotDeviceCmdMonitor.getDeviceId(), iotDeviceCmdMonitor.getSensorAliasId()));
            if (optional.isPresent()) {
                iotDeviceCmdMonitor.setIfAlarm(optional.get().getIfAlarm());
                iotDeviceCmdMonitor.setDateTime(optional.get().getDateTime());
                if ("string".equals(iotDeviceCmdMonitor.getValueType())) {
                    iotDeviceCmdMonitor.setDoubleValue(optional.get().getStringValue());
                } else {
                    iotDeviceCmdMonitor.setDoubleValue(optional.get().getDoubleValue());
                }
                iotDeviceCmdMonitor.setQualityCode(optional.get().getQualityCode());
                iotDeviceCmdMonitor.setUnit(optional.get().getUnit());
            }
        });
        final Map<String, List<IotDeviceCmdMonitor>> listMap = list.stream().collect(Collectors.groupingBy(IotDeviceCmdMonitor::getSensorGroupId));
        final List<Map<String, Object>> collect = listMap.entrySet().stream().map(kv -> Map.of("groupName", map.get(kv.getKey()), "groupData", kv.getValue())).collect(Collectors.toList());
        return AjaxResult.success(collect).put("total", list.size());
    }

    /**
     * Monitor ajax result.
     *
     * @param iotMapVo the iot device cmd vo
     * @return the ajax result
     */
    @GetMapping("/map")
//    @PreAuthorize("@ss.hasPermi('iot:cmd:group')")
    public AjaxResult map(IotMapVo iotMapVo) {
        startPage();
        IotDeviceBo iotDeviceBo = new IotDeviceBo();
        iotDeviceBo.setGroupId(iotMapVo.getSensorId());
        iotDeviceBo.setIndustryId(iotMapVo.getIndustryId());
        List<IotDeviceMapResult> list = iotDeviceService.selectDeviceMapList(iotDeviceBo);
        list.forEach(deviceBo -> {
            deviceBo.setStatus(GuavaCacheUtil.ifTimeOut(String.valueOf(deviceBo.getDeviceId()), deviceBo.getTimeInterval()));
            Optional<IotMonitorDataBo> optional = Optional.ofNullable(GuavaCacheUtil.getMonitorDataMap().get(deviceBo.getDeviceId(), deviceBo.getSensorAliasId()));
            if (optional.isPresent()) {
                deviceBo.setIfAlarm(optional.get().getIfAlarm());
                deviceBo.setDateTime(optional.get().getDateTime());
                deviceBo.setDoubleValue(optional.get().getStringValue());
                deviceBo.setQualityCode(optional.get().getQualityCode());
                deviceBo.setUnit(optional.get().getUnit());
            }
        });
        return AjaxResult.success(list);
    }


    /**
     * 读写指令配置
     *
     * @param iotDeviceCmdUpdateVo the iot device cmd update vo
     * @return the ajax result
     */
    @RepeatSubmit
    @PostMapping("/edit")
    @Transactional(rollbackFor = Exception.class, isolation = Isolation.SERIALIZABLE, timeout = 60)
    public AjaxResult edit(@RequestBody @Valid IotDeviceCmdUpdateVo iotDeviceCmdUpdateVo) {
        AtomicReference<Long> deviceId = new AtomicReference<>(0L);
        iotDeviceCmdUpdateVo.getSensorCmdList().forEach(iotDeviceCmdVo -> {
            IotDeviceCmdBo iotDeviceCmdBo = new IotDeviceCmdBo();
            BeanUtils.copyProperties(iotDeviceCmdVo, iotDeviceCmdBo);
            deviceId.set(iotDeviceCmdBo.getDeviceId());
            iotDeviceCmdService.updateIotDeviceCmd(iotDeviceCmdBo);
        });
        iotHexCmdCreateService.genCmdAndUpdateCmd(deviceId.get());
        TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
            @Override
            public void afterCommit() {
                // 修改指令后重新生成调度任务，重新缓存解析报文规则
                cacheCmdParseMsg.run(Constants.MODBUS_RTU);
                sendModbusCmdSchedule.run(String.valueOf(deviceId.get()));
            }
        });
        return AjaxResult.success();
    }
}
